home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-22 | 29.2 KB | 1,152 lines | [TEXT/MPCC] |
- /*
- ** CTCPStream.cp
- **
- ** TurboTCP support library
- ** TCP stream class
- **
- ** Copyright © 1993-94, FrostByte Design / Eric Scouten
- **
- */
-
-
- #include "CTCPStream.h"
-
- #include "CPtrArray.h"
- #include "CTCPAsyncCall.h"
- #include "CTCPResolverCall.h"
-
-
- #ifndef TCL_NO_TEMPLATES
-
- class CTCPAsyncCallList : public CPtrArray<CTCPAsyncCall> {};
- class CCollaboratorList : public CPtrArray<CCollaborator> {};
- class CTCPStreamList : public CPtrArray<CTCPStream> {};
-
- #pragma template_access public
-
- #pragma template CPtrArray<CTCPAsyncCall>
- #pragma template CPtrArray<CTCPStream>
- #pragma template CPtrArray<CTCPResolverCall>
-
- TCL_DEFINE_CLASS_M1(CPtrArray<CTCPAsyncCall>, CVoidPtrArray);
- TCL_DEFINE_CLASS_M1(CPtrArray<CTCPStream>, CVoidPtrArray);
- TCL_DEFINE_CLASS_M1(CPtrArray<CTCPResolverCall>, CVoidPtrArray);
-
- template <class T>
- CPtrArray<T>::CPtrArray(short theBlockSize)
- : CVoidPtrArray(theBlockSize) { }
-
- template <class T>
- void CPtrArray<T>::DisposeAll() { } // will never be called for these classes
-
- template <class T>
- void CPtrArray<T>::DisposeItems() { } // will never be called for these classes
-
- #else
- TM_DECLARE_CPtrArray(CTCPAsyncCall)
- TM_DECLARE_CPtrArray(CCollaborator)
- TM_DECLARE_CPtrArray(CTCPStream)
- TM_DECLARE_CPtrArray(CTCPResolverCall)
- TM_DEFINE_CPtrArray(CTCPAsyncCall)
- TM_DEFINE_CPtrArray(CTCPStream)
- TM_DEFINE_CPtrArray(CTCPResolverCall)
- #endif
-
- #ifndef __MACTCP__ // for compatibility with old Univ Headers
- #define TCPIOCompletionUPP TCPIOCompletionProcPtr
- #endif
-
-
- // —— UPP for notification procedure (corrects a bug in <TCPPB.h>) ——
-
- #if GENERATINGCFM
-
- UniversalProcPtr CTCPStream::notifyProcUPP =
- NewRoutineDescriptor((ProcPtr) CTCPStream::NotifyProc, uppTCPNotifyProcInfo, GetCurrentISA());
-
- #endif
-
-
- TCL_DEFINE_CLASS_M0(CTCPStream);
-
-
- // —— constructor/destructor ——
-
- /*______________________________________________________________________
- **
- ** constructor
- **
- ** Create a new TCP stream. Allocates the receive buffer (if there’s enough memory).
- **
- ** The stream can be configured to automatically receive data. If auto-receive is used,
- ** the stream object will automatically issue TCPNoCopyRcv calls as needed and respond to
- ** completion notifications when data arrives; data will be returned by the HandleDataArrived
- ** method or by the receive bypass procedure (see InstallRcvBypassProc).
- **
- ** If auto-receive is used, you should not send NoCopyRcv and Rcv messages to this stream.
- ** They may conflict with the auto-receive calls. Auto-receive cannot be turned on or off
- ** once the stream object is created.
- **
- ** theEndpoint (CTCPEndpoint&): endpoint object linked to this stream
- ** recBufferSize (long): size of the receive buffer we need; use 0 or
- ** less then 4K to get the minimum 4K buffer
- ** autoReceiveSize (short): number of entries in RDS for auto-receive,
- ** 0 to disable autoreceiving
- ** autoReceiveNum (short): number of auto-receive calls to issue at once
- ** must be at least 1
- **
- */
-
- CTCPStream::CTCPStream(CTCPEndpoint& theEndpoint, long recBufferSize,
- short autoReceiveSize, short autoReceiveNum)
- : itsEndpoint(&theEndpoint), qNotifyEntry(this), qDisposeEntry(this)
-
- {
-
- // clear all of our variables
-
- macTCPStream = NULL;
- itsBuffer = NULL;
- itsAsyncCalls = NULL;
- hasSessionOpen = pendingOpen = pendingClose = remoteClose
- = pendingAbort = pendingNotify = pendingDispose = FALSE;
-
- itsBufferSize = 0L;
- rcvUrgent = FALSE;
-
- itsULPtimeout = 0;
- itsULPaction = 0;
- itsValidityFlag = 0;
- itsCommandTimeoutValue = 0;
- itsTosFlags = 0;
- itsPrecedence = 0;
- itsDontFrag = 0;
- itsTimeToLive = 0;
- itsSecurity = 0;
- itsOptionCnt = 0;
- sendNextUrgent = 0;
- sendNextPush = FALSE;
-
- notifClosing = FALSE;
- notifTimeout = FALSE;
- notifTerminate = FALSE;
- notifDataArrived = FALSE;
- notifUrgent = FALSE;
- notifICMP = FALSE;
- disposeOnTerminate = FALSE;
- receivedClose = FALSE;
- receivedTerminate = FALSE;
- itsAutoReceiveSize = Min(autoReceiveSize, autoReceiveMax);
- itsAutoReceiveNum = Max(autoReceiveNum, 1);
-
- qNotifyEntry.qType = notifyStream;
- qDisposeEntry.qType = disposeStream;
-
- itsAsyncCalls = new CTCPAsyncCallList;
-
-
- // create the receive buffer
-
- if (recBufferSize < minReceiveSize) // if caller requested a buffer that is too small,
- recBufferSize = minReceiveSize; // beef it up to acceptable size (4K)
- itsBuffer = NewHandle(recBufferSize);
- FailNIL(itsBuffer);
- itsBufferSize = recBufferSize;
-
-
- /*
- ** Don’t create TCP stream here. Therefore, if a session is never opened and the program
- ** crashes (or ExitToShell is used by the programmer), MacTCP will remain stable.
- */
-
- TCL_END_CONSTRUCTOR;
-
- }
-
-
- /*______________________________________________________________________
- **
- ** destructor (private method)
- **
- ** DO NOT CALL THIS METHOD! Use Dispose() instead.
- **
- */
-
- CTCPStream::~CTCPStream()
- {
- TCL_START_DESTRUCTOR;
- }
-
-
- /*______________________________________________________________________
- **
- ** Dispose
- **
- ** Get rid of a TCP stream. Deallocates the receive buffer. If a session is still open,
- ** issues an Abort command, then postpones its disposal so completion routines still
- ** have a valid object to call.
- **
- */
-
- void CTCPStream::Dispose()
-
- {
- TCPiopb theReleaseParam;
-
- // assume that the endpoint object is going away also
-
- itsEndpoint = NULL;
-
-
- // we’re no longer in delayed disposal queue
-
- pendingDispose = FALSE;
-
-
- // initiate a graceful close if necessary
-
- if (hasSessionOpen && !receivedClose) {
- Close();
- disposeOnTerminate = TRUE;
- return;
- }
-
-
- // abort any connections that are yet to open
-
- if (pendingOpen) {
- Abort();
- disposeOnTerminate = TRUE;
- return;
- }
-
-
- // if calls are still outstanding, wait a while
-
- if (!itsAsyncCalls->IsEmpty()) {
- if ((macTCPStream) && pendingOpen) { // get rid of the thing…
- Abort();
- PostponeDispose();
- return;
- }
- if ((macTCPStream) && pendingOpen) {
- DoSyncCall(TCPRelease, &theReleaseParam); // perhaps this will shake it loose
- macTCPStream = NULL; // 2.0b5 bug fix: remember that we killed the stream
- }
- PostponeDispose();
- return;
- }
-
-
- // if connection is still there, release stream to see if that shakes things loose
-
- if (macTCPStream && hasSessionOpen && !receivedTerminate) {
- DoSyncCall(TCPRelease, &theReleaseParam);
- macTCPStream = NULL;
- PostponeDispose();
- return;
- }
-
-
- // make sure that terminate notice has been received
-
- if (hasSessionOpen && !receivedTerminate) {
- PostponeDispose();
- return;
- }
-
-
- // release the stream from MacTCP
-
- if (macTCPStream)
- DoSyncCall(TCPRelease, &theReleaseParam);
- CTCPDriver::gTCPDriver->RemoveActiveStream(this);
-
-
- // forget the async calls list
-
- ForgetObject(itsAsyncCalls);
-
-
- // release the receive buffer
-
- ForgetHandle(itsBuffer);
- macTCPStream = NULL;
- itsBufferSize = 0L;
-
- delete this;
- }
-
-
- // —— basic user TCP calls ——
-
- /*______________________________________________________________________
- **
- ** OpenConnection
- **
- ** Opens a TCP session (either passive or active). Note that MacTCP seems to fail when
- ** non-zero values are used for theRemoteIP and theRemotePort.
- **
- ** passive (Boolean): TRUE for passive open; FALSE for active open
- ** theRemoteIP (ip_addr): IP address of remote host (0 for any host)
- ** theRemotePort (b_16): TCP port of remote host (0 for any port)
- ** theLocalPort (b_16): TCP port for local host (0 for any port)
- **
- */
-
- void CTCPStream::OpenConnection(Boolean passive, ip_addr theRemoteIP,
- b_16 theRemotePort, b_16 theLocalPort)
-
- {
- TCPiopb theCreateParam;
- TCPiopb theOpenParam;
- OSErr theResult;
-
-
- // ensure that MacTCP is awake & ready for us
-
- if (!(CTCPDriver::gTCPDriver->CheckTCPDriver()))
- FailOSErr(noTCPError);
-
-
- // if the stream hasn’t been created yet, create one now
-
- if (!macTCPStream) {
- MoveHHi(itsBuffer); // lock the receive buffer
- HLock(itsBuffer);
-
- theCreateParam.csParam.create.rcvBuff = *itsBuffer;
- theCreateParam.csParam.create.rcvBuffLen = itsBufferSize;
- #if GENERATINGCFM
- theCreateParam.csParam.create.notifyProc = (TCPNotifyUPP) notifyProcUPP;
- #else
- theCreateParam.csParam.create.notifyProc = &NotifyProc;
- #endif
- theCreateParam.csParam.create.userDataPtr = (Ptr) this;
-
- macTCPStream = (Ptr) 1; // preempt the check for no stream present
- theResult = DoSyncCall(TCPCreate, &theCreateParam);
-
- if (theResult == noErr) { // did we get a valid stream?
- CTCPDriver::gTCPDriver->RegisterActiveStream(this); // yes, register in global stream list
- macTCPStream = (Ptr) theCreateParam.tcpStream;
- } else {
- macTCPStream = NULL;
- HandleOpenFailed(theResult);
- }
- }
-
-
- // don’t allow open if already opening
-
- if (!(hasSessionOpen || pendingOpen)) {
-
- // fill in parms to open call
-
- theOpenParam.csParam.open.ulpTimeoutValue = itsULPtimeout;
- theOpenParam.csParam.open.ulpTimeoutAction = itsULPaction;
- theOpenParam.csParam.open.validityFlags = itsValidityFlag;
- theOpenParam.csParam.open.commandTimeoutValue = itsCommandTimeoutValue;
- theOpenParam.csParam.open.remoteHost = theRemoteIP;
- theOpenParam.csParam.open.remotePort = theRemotePort;
- theOpenParam.csParam.open.localPort = theLocalPort;
- theOpenParam.csParam.open.tosFlags = itsTosFlags;
- theOpenParam.csParam.open.precedence = itsPrecedence;
- theOpenParam.csParam.open.dontFrag = itsDontFrag;
- theOpenParam.csParam.open.timeToLive = itsTimeToLive;
- theOpenParam.csParam.open.security = itsSecurity;
- theOpenParam.csParam.open.optionCnt = itsOptionCnt;
- BlockMove(&itsOptions, &theOpenParam.csParam.open.options, 40);
- theOpenParam.csParam.open.userDataPtr = (Ptr) this;
- pendingOpen = TRUE;
- receivedClose = receivedTerminate = FALSE;
-
- // perform the call
-
- theResult = DoAsyncCall((passive ? TCPPassiveOpen : TCPActiveOpen), &theOpenParam);
- if (theResult)
- HandleOpenFailed(theResult);
-
- // pull IP address/port info from remote host
-
- itsRemoteIP = theOpenParam.csParam.open.remoteHost;
- itsRemotePort = theOpenParam.csParam.open.remotePort;
- itsLocalIP = theOpenParam.csParam.open.localHost;
- itsLocalPort = theOpenParam.csParam.open.localPort;
- }
- else
- HandleOpenFailed(connectionExists);
-
- }
-
-
- /*______________________________________________________________________
- **
- ** Close
- **
- ** Signal that the connection should be closed. The stream’s owner should wait until it
- ** receives the tcpStreamClosed message before disposing of the stream object.
- ** This will allow time for the host to close gracefully.
- **
- */
-
- void CTCPStream::Close()
-
- {
- TCPiopb theCloseParam;
- OSErr theResult;
-
- if ((hasSessionOpen && (!pendingAbort) && (!pendingClose)) || remoteClose) {
- pendingClose = TRUE;
- pendingOpen = remoteClose = FALSE;
- theCloseParam.csParam.close.ulpTimeoutValue = itsULPtimeout;
- theCloseParam.csParam.close.ulpTimeoutAction = itsULPaction;
- theCloseParam.csParam.close.validityFlags = itsValidityFlag;
- theCloseParam.csParam.close.userDataPtr = (Ptr) this;
- theResult = DoAsyncCall(TCPClose, &theCloseParam);
- if ((theResult != noErr) && (theResult != connectionDoesntExist))
- HandleTCPError(theResult, TCPClose);
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** Abort
- **
- ** Cancel the connection immediately. This call is performed synchronously. USE THIS CALL
- ** WITH CAUTION! MacTCP seems to get quite confused when a session is closed and
- ** data hasn’t all been received.
- **
- */
-
- void CTCPStream::Abort()
-
- {
- TCPiopb theAbortParam;
- OSErr theResult;
-
- if ((hasSessionOpen || pendingOpen) && (!pendingAbort)) {
- pendingAbort = TRUE;
- pendingOpen = pendingClose = remoteClose = FALSE;
- theAbortParam.csParam.abort.userDataPtr = (Ptr) this;
- theResult = DoSyncCall(TCPAbort, &theAbortParam);
- if (theResult)
- HandleTCPError(theResult, TCPAbort);
- else
- receivedClose = TRUE;
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** NoCopyRcv
- **
- ** Receive data without copying from TCP’s internal buffers. The completion routine in
- ** CTCPAsyncCall will take care of returning the RDS automatically. IF YOU ARE USING
- ** AUTO-RECEIVE, DO NOT CALL THIS METHOD!
- **
- ** itsRDS (rdsEntry*): receive data structure (see MacTCP manual, p30)
- ** itsRDSSize (b_16): number of entries in RDS (6 bytes each)
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- **
- */
-
- void CTCPStream::NoCopyRcv(rdsEntry* itsRDS, b_16 itsRDSSize, b_16 itsTimeOut)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
- theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
- theRcvParam.csParam.receive.rdsPtr = (Ptr) itsRDS;
- theRcvParam.csParam.receive.rdsLength = (unsigned short) Min(itsRDSSize, autoReceiveMax);
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
- theRcvParam.csParam.open.options[36] = (Byte) itsAutoReceiveSize;
-
- theResult = DoAsyncCall(TCPNoCopyRcv, &theRcvParam);
- if ((theResult != noErr) && (theResult != connectionClosing))
- HandleTCPError(theResult, TCPNoCopyRcv);
-
- }
-
-
- /*______________________________________________________________________
- **
- ** BfrReturn
- **
- ** Return a receive buffer to MacTCP used by the NoCopyRcv method. You should never need
- ** to call this method, since it is done automatically by the CTCPAsyncCall::Dispatch()
- ** method which handles all asynchronous completions.
- **
- ** itsRDS (Ptr): the RDS structure to return
- **
- */
-
- void CTCPStream::BfrReturn(Ptr itsRDS)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
-
- // reject attempt to return buffers after the session is closed…
- // MacTCP has done it already (I think)
-
- if (hasSessionOpen) {
- theRcvParam.csParam.receive.rdsPtr = itsRDS;
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
-
- DoSyncCall(TCPRcvBfrReturn, &theRcvParam);
- // ignore errors — this is bad practice, but…
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** Send
- **
- ** Send data on the TCP stream using the Write Data Structure (WDS) structure. The
- ** asynchronous completion routine can optionally dispose (DisposPtr) of the WDS and its
- ** associated data structures once the write operation is completed. If so, the buffers you
- ** provide must be expendable.
- **
- ** itsWDS (wdsEntry*): write data structure (see MacTCP manual, p30)
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- ** disposeWDS (Boolean): dispose of WDS and data structures when completed
- ** notifyWhenDone (Boolean): notify endpoint object when send operation completes
- ** (use HandleDataSent or HandleSendFailed)
- **
- */
-
- void CTCPStream::Send(wdsEntry* itsWDS, b_16 itsTimeOut, Boolean disposeWDS,
- Boolean notifyWhenDone)
-
- {
- TCPiopb theSendParam;
- OSErr theResult;
-
-
- // ensure that at least one byte is being sent
-
-
- if ((*itsWDS).length == 0)
- return;
-
-
- // fill in parms to send call
-
- theSendParam.csParam.send.ulpTimeoutValue = itsTimeOut;
- theSendParam.csParam.send.ulpTimeoutAction = itsULPaction;
- theSendParam.csParam.send.validityFlags = itsValidityFlag;
- theSendParam.csParam.send.pushFlag = sendNextPush;
- theSendParam.csParam.send.urgentFlag = sendNextUrgent;
- theSendParam.csParam.send.wdsPtr = (Ptr) itsWDS;
- theSendParam.csParam.send.userDataPtr = (Ptr) this;
-
- theSendParam.csParam.open.timeToLive = notifyWhenDone;
- theSendParam.csParam.open.security = disposeWDS;
- // ^^^^ this isn’t pretty, but it’s efficient
-
- sendNextPush = FALSE;
- sendNextUrgent = 0;
-
-
- // perform the call
-
- theResult = DoAsyncCall(TCPSend, &theSendParam);
- if (theResult)
- HandleTCPError(theResult, TCPSend);
- }
-
-
- /*______________________________________________________________________
- **
- ** Receive
- **
- ** Receive data and copy to a user buffer.
- **
- ** theData (Ptr): receive buffer
- ** theDataSize (b_16): size of receive buffer in bytes
- ** itsTimeOut (b_16): command timeout value in seconds (0 = infinite)
- **
- */
-
- void CTCPStream::Receive(Ptr theData, b_16 itsDataSize, b_16 itsTimeOut)
-
- {
- TCPiopb theRcvParam;
- OSErr theResult;
-
- theRcvParam.csParam.receive.commandTimeoutValue = itsTimeOut;
- theRcvParam.csParam.receive.rcvBuff = theData;
- theRcvParam.csParam.receive.rcvBuffLen = (unsigned short) itsDataSize;
- theRcvParam.csParam.receive.userDataPtr = (Ptr) this;
-
- theResult = DoAsyncCall(TCPRcv, &theRcvParam);
- if (theResult)
- HandleTCPError(theResult, TCPRcv);
- }
-
-
- /*______________________________________________________________________
- **
- ** Status
- **
- ** Returns a large pile of information about the TCP connection.
- **
- ** theStatusBlock (TCPStatusPB*): user buffer for TCP status info
- **
- */
-
- void CTCPStream::Status (TCPStatusPB* theStatusBlock)
-
- {
- TCPiopb theStatusParam;
- short i;
- Ptr p;
- OSErr result;
-
- result = DoSyncCall(TCPStatus, &theStatusParam);
- BlockMove(&theStatusParam.csParam, theStatusBlock, 66);
- if ((result) || (theStatusParam.ioResult != noErr)) {
- i = 0;
- p = (Ptr) theStatusBlock;
- while (i++<66)
- *(p++) = 0;
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** ConnectionState
- **
- ** Returns the current status of the MacTCP stream. For definitions of the state codes,
- ** see TCP reference manual, p56.
- **
- ** return (b_16): status of TCP stream
- **
- */
-
- b_16 CTCPStream::ConnectionState()
-
- {
- TCPStatusPB theStatusBlock;
-
- Status(&theStatusBlock);
- return theStatusBlock.connectionState;
- }
-
-
- // —— specialized functions for sending data ——
-
- /*______________________________________________________________________
- **
- ** SendBfrCpy
- **
- ** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method and copies
- ** the bytes from the caller’s buffer to a data buffer which can persist beyond the procedure
- ** or object which generated the data.
- **
- ** theData (const void*): the bytes to send
- ** theDataSize (unsigned short): number of bytes to send
- **
- */
-
- void CTCPStream::SendBfrCpy(const void* theData, unsigned short theDataSize)
-
- {
- wdsEntry* itsWDS = NULL;
- Ptr itsNewBuffer = NULL;
-
-
- // make sure there’s something to send
-
- if (theDataSize == 0)
- return;
-
- TRY {
-
- // create the two buffers
-
- itsWDS = (wdsEntry*) NewPtr(8); // WDS with room for one entry
- itsNewBuffer = NewPtr(theDataSize);
-
-
- // copy stuff to new buffer
-
- BlockMove(theData, itsNewBuffer, theDataSize);
- (*itsWDS).length = theDataSize;
- (*itsWDS).ptr = itsNewBuffer;
- (*(++itsWDS)).length = 0;
- --itsWDS;
-
-
- // send data, using default timeout value
-
- Send(itsWDS, itsULPtimeout, TRUE, FALSE);
- // TurboTCP 2.0 NOTE: completion of Send
- // is no longer called
- }
-
- CATCH {
- ForgetPtr(itsWDS);
- ForgetPtr(itsNewBuffer);
- }
- ENDTRY;
-
- }
-
-
- /*______________________________________________________________________
- **
- ** SendBfrNoCpy
- **
- ** Send a range of bytes to the TCP stream. Prepares a WDS for the Send method. Does not
- ** copy the bytes.
- **
- ** theData (const void*): the bytes to send
- ** theDataSize (unsigned short): number of bytes to send
- ** disposeWhenDone (Boolean): set to TRUE to dispose of the buffer (DisposPtr)
- ** when completed (only if this pointer was
- ** allocated as a pointer, not a handle deref)
- ** notifyWhenDone (Boolean): set to TRUE to notify the endpoint object
- ** when completed (via HandleDataSent or
- ** HandleSendFailed)
- **
- */
-
- void CTCPStream::SendBfrNoCpy(const void* theData, unsigned short theDataSize,
- Boolean disposeWhenDone, Boolean notifyWhenDone)
-
- {
- wdsEntry *itsWDS = NULL;
-
-
- // make sure there’s something to send
-
- if (theDataSize == 0)
- return;
-
- TRY {
-
- // create the WDS buffer & fill it in
-
- itsWDS = (wdsEntry*) NewPtr(8); // WDS with room for one entry
-
- (*itsWDS).length = theDataSize;
- (*itsWDS).ptr = (Ptr) theData;
- (*(++itsWDS)).length = 0;
- --itsWDS;
-
-
- // send data, using default timeout value
-
- Send(itsWDS, itsULPtimeout, disposeWhenDone, notifyWhenDone);
- }
-
- CATCH {
- ForgetPtr(itsWDS);
- }
- ENDTRY;
-
- }
-
-
- // —— notification routines ——
-
- /*______________________________________________________________________
- **
- ** HandleDataSent (private method)
- **
- ** Respond to successful data sending. Notifies endpoint object and disposes of data if
- ** requested. Disposes of WDS structure.
- **
- ** WDSPtr (wdsEntry*): the WDS structure
- ** disposeWhenDone (Boolean): true to dispose of objects
- ** notifyWhenDone (Boolean): true to notify endpoint object
- **
- */
-
- void CTCPStream::HandleDataSent(wdsEntry* WDSPtr, Boolean disposeWhenDone,
- Boolean notifyWhenDone)
-
- {
- wdsEntry* theDisposePtr = WDSPtr;
- while ((*theDisposePtr).length) {
- if ((notifyWhenDone) && (itsEndpoint)) // 2.0b4
- itsEndpoint->HandleDataSent((*theDisposePtr).ptr, (*theDisposePtr).length);
- if (disposeWhenDone)
- ForgetPtr((*theDisposePtr).ptr);
- theDisposePtr++;
- }
- ForgetPtr(WDSPtr);
- }
-
-
- /*______________________________________________________________________
- **
- ** HandleSendFailed (private method)
- **
- ** Respond to failure to send data. Notifies endpoint object and disposes of data if requested.
- ** Disposes of WDS structure.
- **
- ** WDSPtr (wdsEntry*): the WDS structure
- ** disposeWhenDone (Boolean): true to dispose of objects
- ** notifyWhenDone (Boolean): true to notify endpoint object
- ** theResultCode (OSErr): the reason for failure
- **
- */
-
- void CTCPStream::HandleSendFailed(wdsEntry* WDSPtr, Boolean disposeWhenDone,
- Boolean notifyWhenDone, OSErr theResultCode)
-
- {
- // send error notification (once only)
-
- if (itsEndpoint) // 2.0b4
- itsEndpoint->HandleTCPError(theResultCode, TCPSend);
- else // 2.0b4
- disposeWhenDone = true; // 2.0b4
-
-
- // notify and dispose each data packet in WDS
-
- wdsEntry* theDisposePtr = WDSPtr;
- while ((*theDisposePtr).length) {
- if ((notifyWhenDone) && (itsEndpoint)) // 2.0b4
- itsEndpoint->HandleSendFailed((*theDisposePtr).ptr, (*theDisposePtr).length, theResultCode);
- if (disposeWhenDone)
- ForgetPtr((*theDisposePtr).ptr);
- theDisposePtr++;
- }
- ForgetPtr(WDSPtr);
-
- }
-
-
- // —— private methods for initiating TCP calls ——
-
- /*______________________________________________________________________
- **
- ** DoAsyncCall (private method)
- **
- ** Creates a CTCPAsyncCall object and uses it to execute a TCP Device Manager call
- ** asynchronously. Does not wait for completion. Fails if not enough memory to
- ** create call object.
- **
- ** theCsCode (b_16): TCP operation code
- ** theParamBlockPtr (TCPiopb*): parameter block for TCP call
- **
- ** return (OSErr): result code (+1 is not returned)
- **
- */
-
- OSErr CTCPStream::DoAsyncCall(b_16 theCsCode, TCPiopb* theParamBlockPtr)
-
- {
- OSErr theResult;
- CTCPAsyncCall* theCall;
-
-
- // make sure a stream was opened
-
- if (macTCPStream)
- (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
- else
- return invalidStreamPtr;
-
-
- // create call object
-
- theCall = new CTCPAsyncCall(*this);
- theResult = theCall->DoAsyncCall(theCsCode, theParamBlockPtr);
- if (theResult == inProgress)
- itsAsyncCalls->Add(theCall);
- return (theResult == inProgress) ? noErr : theResult;
- }
-
-
- /*______________________________________________________________________
- **
- ** DoSyncCall (private method)
- **
- ** Fills in the standard parameters for a TCP Device Manager call and executes the call.
- ** Waits for completion of the call.
- **
- ** theCsCode (b_16): TCP operation code
- ** theParamBlockPtr (TCPiopb*): parameter block for TCP call
- **
- ** return (OSErr): result code (+1 is not returned)
- **
- */
-
- OSErr CTCPStream::DoSyncCall(b_16 theCsCode, TCPiopb* theParamBlockPtr)
-
- {
-
- // make sure a stream was opened
-
- if (macTCPStream)
- (*theParamBlockPtr).tcpStream = (StreamPtr) macTCPStream;
- else
- return invalidStreamPtr;
-
-
- // perform the call
-
- (*theParamBlockPtr).ioCompletion = (TCPIOCompletionUPP) NULL;
- (*theParamBlockPtr).ioCRefNum = CTCPDriver::gTCPDriver->GetTCPRefNum();
- (*theParamBlockPtr).csCode = theCsCode;
- PBControlSync((ParmBlkPtr) theParamBlockPtr);
- return (*theParamBlockPtr).ioResult;
- }
-
-
- /*______________________________________________________________________
- **
- ** IssueAutoReceive (private method)
- **
- ** Issue a NoCopyRcv command to grab the next batch of data. Does nothing if auto-receive
- ** is not enabled.
- **
- */
-
- void CTCPStream::IssueAutoReceive()
-
- {
- // create a new RDS with room for # of entries requested & send it to TCP
-
- Ptr newRDS;
- if (hasSessionOpen && itsAutoReceiveSize) {
- FailNIL(newRDS = NewPtr((itsAutoReceiveSize*6)+2));
- NoCopyRcv((rdsEntry*) newRDS, itsAutoReceiveSize, 0);
- }
- }
-
-
- /*______________________________________________________________________
- **
- ** ProcessNotify
- **
- ** Respond to notifications received by the ASR during interrupt time. This routine is free
- ** of interrupt-level constraints.
- **
- */
-
- void CTCPStream::ProcessNotify()
-
- {
- // no longer in notify queue
-
- pendingNotify = FALSE;
-
-
- // if terminated, inform the stream owner & dispose (if appropriate)
- // clear all other notifications (they no longer apply)
-
- if (notifTerminate) {
- HandleTerminated(notifTermReason);
- hasSessionOpen = pendingOpen = pendingClose =
- remoteClose = pendingAbort = notifClosing = notifTimeout =
- notifDataArrived = notifUrgent = FALSE;
- receivedClose = receivedTerminate = TRUE;
- if (disposeOnTerminate)
- PostponeDispose();
- }
-
-
- // if closed, inform stream owner, wait for termination before disposing (if appropriate)
-
- if (notifClosing) {
- HandleClosing(remoteClose);
- receivedClose = TRUE;
- notifClosing = FALSE;
- if (disposeOnTerminate)
- PostponeDispose();
- }
-
-
- // if urgent notification, flag beginning of urgent data
-
- if (notifUrgent) {
- if (!rcvUrgent) {
- RcvUrgentBegin();
- HandleUrgentBegin();
- }
- notifUrgent = FALSE;
- }
-
-
- // tell stream owner about other notifications, TurboTCP doesn’t respond to these
-
- if (notifTimeout) {
- HandleTimeout();
- notifTimeout = FALSE;
- }
-
- if (notifDataArrived) {
- HandleUnexpectedData();
- notifDataArrived = FALSE;
- }
-
- if (notifICMP) {
- HandleICMP(¬ifICMPreport);
- notifICMP = FALSE;
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** ProcessAsyncCompletion
- **
- ** Respond to the completion of an asynchronous call. Removes the async call object from
- ** the list of outstanding calls.
- **
- ** theCall (CTCPAsyncCall*): the call which was completed
- **
- */
-
- void CTCPStream::ProcessAsyncCompletion(CTCPAsyncCall* theCall)
-
- {
- itsAsyncCalls->Remove(theCall);
- }
-
-
- /***********************************************************************
- ************************************************************************
- **
- ** INTERRUPT-LEVEL routines follow. These routines cannot make memory allocations, cannot make
- ** synchronous TCP calls, and cannot use the Think C profiler.
- **
- */
-
- #ifndef __MWERKS__
- //#pragma options(!profile)
- #else
- #pragma profile off
- #endif
-
-
- /*______________________________________________________________________
- **
- ** PostponeDispose
- **
- ** Add this stream object to the delayed disposal queue. This routine may be called during
- ** an interrupt.
- **
- */
-
- void CTCPStream::PostponeDispose()
-
- {
- if (pendingDispose) // ignore this if we’re already in disposal queue
- return;
- pendingDispose = TRUE;
- disposeOnTerminate = TRUE;
- Enqueue((QElemPtr) &qDisposeEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
- }
-
-
- /*______________________________________________________________________
- **
- ** PostponeNotify (protected method)
- **
- ** The asynchronous notification routine (ASR). Receives notification of events for all TCP
- ** streams. Dispatches the notification to the proper method of the CTCPStream object. This
- ** routine is installed for all TCP streams created by the CTCPStream object.
- **
- ** This method merely logs the kind of notification and adds this stream to the list of streams
- ** to be processed next time through the event loop.
- **
- ** eventCode (b_16): event code (see MacTCP manual, p37)
- ** terminReason (b_16): reason for termination (if applicable)
- ** icmpMsg (structICMPReport*): ICMP report (if applicable)
- **
- */
-
- void CTCPStream::PostponeNotify(b_16 eventCode, b_16 terminReason, struct ICMPReport* icmpMsg)
-
- {
-
- // reject notifications for unexpected data if auto-receiving
-
- if ((itsAutoReceiveSize > 0) && (eventCode == TCPDataArrival))
- return;
-
-
- // install this stream in asynchronous events queue
- // CTCPDriver::ProcessNetEvents will pick it up later
-
- if (!pendingNotify) {
- Enqueue((QElemPtr) &qNotifyEntry, &(CTCPDriver::gTCPDriver->asyncQueue));
- pendingNotify = TRUE;
- }
-
-
- // record the event type
-
- switch (eventCode) {
- case TCPClosing:
- remoteClose = remoteClose || !pendingClose;
- notifClosing = pendingClose = TRUE;
- break;
-
- case TCPULPTimeout:
- notifTimeout = TRUE;
- break;
-
- case TCPTerminate:
- notifTerminate = TRUE;
- notifTermReason = terminReason;
- break;
-
- case TCPDataArrival:
- notifDataArrived = TRUE;
- break;
-
- case TCPUrgent:
- notifUrgent = TRUE;
- break;
-
- case TCPICMPReceived:
- notifICMP = TRUE;
- BlockMove(icmpMsg, ¬ifICMPreport, 24);
- break;
- }
-
- }
-
-
- /*______________________________________________________________________
- **
- ** NotifyProc (private static method)
- **
- ** The asynchronous notification routine (ASR). This static method just decodes the
- ** userDataPtr and calls PostponeNotify for the object named. This routine is the standard
- ** ASR for all TCP streams created by the CTCPStream object.
- **
- ** tcpStream (StreamPtr): the TCP stream in question
- ** eventCode (short): TCP event code (see MacTCP Dev guide, p37)
- ** userDataPtr (Ptr): user data pointer (in this case, the CTCPStream)
- ** terminReason (short): reason for termination (see MacTCP Dev guide, p37)
- ** icmpMsg (…): ICMP report (if applicable)
- **
- */
-
- pascal void CTCPStream::NotifyProc(StreamPtr tcpStream, unsigned short eventCode,
- Ptr userDataPtr, unsigned short terminReason,
- struct ICMPReport* icmpMsg)
-
- {
- CTCPStream* theTCPStream = (CTCPStream*) userDataPtr;
- theTCPStream->PostponeNotify(eventCode, terminReason, icmpMsg);
- }
-